package hu.myai.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import org.joone.engine.Layer;
import org.joone.engine.Synapse;
import org.joone.io.MemoryInputSynapse;
import org.joone.io.StreamInputSynapse;
import org.joone.log.ILogger;
import org.joone.log.LoggerFactory;
import org.joone.net.NeuralNet;
import org.joone.util.IncrementalPercentPlugIn;
import org.joone.util.LearningSwitch;
import org.joone.util.MinMaxExtractorPlugIn;

public class SanfordNet {

	private static final ILogger log = LoggerFactory.getLogger(SanfordNet.class);

	/* set the lenght of the observing time window */
	private static int timeSeries = 10;

	public int getTimeSeries() {
		return timeSeries;
	}

	/** restore a nnet from a file */
	public NeuralNet restoreNeuralNet(String fileName) {
		NeuralNet nnet = null;
		try {
			FileInputStream stream = new FileInputStream(fileName);
			ObjectInput input = new ObjectInputStream(stream);
			nnet = (NeuralNet) input.readObject();
		} catch (Exception e) {
			log.warn("Exception was thrown. Message is : " + e.getMessage(), e);
		}
		return nnet;
	}

	/** * save the nnet to file */
	public void saveNeuralNet(NeuralNet NN, String fileName) {
		try {
			FileOutputStream stream = new FileOutputStream(fileName);
			ObjectOutputStream out = new ObjectOutputStream(stream);
			out.writeObject(NN);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/** * Connects two Layers with a Synapse */
	public void connect(Layer ly1, Synapse syn, Layer ly2) {
		ly1.addOutputSynapse(syn);
		ly2.addInputSynapse(syn);
	}

	/** * Creates a Sanford input Synapse */
	public MemoryInputSynapse createInput(double[][] data, int firstRow, int firstCol, int lastCol) {

		MemoryInputSynapse input = new MemoryInputSynapse();

		input.setInputArray(data);
		input.setFirstRow(firstRow);

		if (firstCol != lastCol)
			input.setAdvancedColumnSelector(firstCol + "-" + lastCol);
		else
			input.setAdvancedColumnSelector(Integer.toString(firstCol));

		// percentage of the daily increase
		IncrementalPercentPlugIn incPcr = new IncrementalPercentPlugIn();
		if (firstCol != lastCol) {
			incPcr.setAdvancedSerieSelector("1-" + (lastCol - firstCol + 1));
			input.addPlugIn(incPcr);
		} else {
			incPcr.setAdvancedSerieSelector("1");
			input.addPlugIn(incPcr);
		}

		return input;
	}

	/** * Creates a Sanford input Synapse */
	public MemoryInputSynapse createValidInput(double[][] data, int firstRow, int firstCol, int lastCol) {

		MemoryInputSynapse input = new MemoryInputSynapse();

		input.setInputArray(data);
		input.setFirstRow(firstRow);
		input.setAdvancedColumnSelector(firstCol + "-" + lastCol);

		// We generate an output min max
		MinMaxExtractorPlugIn minmax = new MinMaxExtractorPlugIn();
		minmax.setAdvancedSerieSelector("1");
		minmax.setMinChangePercentage(0.03);
		input.addPlugIn(minmax);

		if (firstCol != lastCol) {
			// We generate an output incremental pct
			IncrementalPercentPlugIn incPcr = new IncrementalPercentPlugIn();
			incPcr.setAdvancedSerieSelector("2-" + (lastCol - firstCol + 1));
			input.addPlugIn(incPcr);
		}
		return input;
	}

	/*
	 * Creates a LearningSwitch and attach to it both the training and the
	 * desired input synapses
	 */
	public LearningSwitch createSwitch(StreamInputSynapse IT, StreamInputSynapse IV) {
		LearningSwitch lsw = new LearningSwitch();
		lsw.addTrainingSet(IT);
		lsw.addValidationSet(IV);
		return lsw;
	}

	public static Date getStartDate() {
		GregorianCalendar initdate = new GregorianCalendar();

		int days = Math.round(Constans.weekDays * 7 / 5) + 5;
		initdate.add(Calendar.DATE, -days);

		return initdate.getTime();
	}

	public double calcBack(double x1, double x2, double y) {

		double x3 = 1E5;
		x3 /= (1 / y) - 1;
		x3 = Math.log10(x3);
		x3 /= Math.log10(1E5);
		x3 /= x1;
		x3 /= x2;
		return x3;
	}

	public String trim(double input) {
		return trim(input, 5);
	}

	public String trim(double input, int length) {

		BigDecimal inputBig = BigDecimal.valueOf(input);

		if (input >= 0)
			return " " + inputBig.setScale(length, RoundingMode.FLOOR).toPlainString();
		else
			return inputBig.setScale(length, RoundingMode.FLOOR).toPlainString();

	}

	public List<String> getAllSymbol() {

		List<String> resValue = new ArrayList<String>();

		// it makes it all
		String forCheckSymbol = Constans.forCheckSymbol.replaceAll("[ \t\n]+", " ");
		String[] symbolArray = forCheckSymbol.split(" ");

		for (int i = 0; i < symbolArray.length; i++) {
			resValue.add(symbolArray[i]);
		}

		return resValue;
	}

	public List<String> getNnetFiles(String symbol) {
		List<String> resValue = new ArrayList<String>();

		File path = new File(Constans.nnetFilesDir + File.separator + symbol);
		if (path.isDirectory()) {
			// iterate thru contents of folder
			String[] dirContainer = path.list();
			for (int j = 0; j < dirContainer.length; j++) {

				if (dirContainer[j].toLowerCase().endsWith(".snet")) {
					resValue.add(path.getAbsoluteFile() + File.separator + dirContainer[j]);
					break;
				}
			}
		} else {
			log.error("Error - not a directory: " + Constans.nnetFilesDir + File.separator + symbol);
		}

		return resValue;
	}

	public List<String> getNnetFiles(List<String> symbolList) {
		List<String> resValue = new ArrayList<String>();

		for (String symbol : symbolList) {
			resValue.addAll(getNnetFiles(symbol));
		}

		return resValue;
	}

	public Date geTZeroDate() {
		Date firstDate;
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
			firstDate = sdf.parse(Constans.zeroDate);
		} catch (ParseException ex) {
			firstDate = new Date(0);
		}
		return firstDate;
	}
}